from . import uiautomation as uia
from .languages import *
from .utils import *
from .color import *
from .errors import *
import datetime
import time
import os
import re
from OutPut.outPut import op

# 导入屏幕解锁功能
try:
    import sys
    sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    from screen_unlock import screen_lock_manager
    from wechat_config import config
    SCREEN_UNLOCK_AVAILABLE = True
    op("[*] 屏幕解锁功能已加载")
except ImportError as e:
    SCREEN_UNLOCK_AVAILABLE = False
    op(f"[!] 屏幕解锁功能加载失败: {e}")
    # 创建一个模拟的配置对象
    class MockConfig:
        SCREEN_LOCK_CHECK_ENABLED = False
        AUTO_UNLOCK_ENABLED = False
    config = MockConfig()


class WxParam:
    SYS_TEXT_HEIGHT = 33
    TIME_TEXT_HEIGHT = 34
    RECALL_TEXT_HEIGHT = 45
    CHAT_TEXT_HEIGHT = 52
    CHAT_IMG_HEIGHT = 117
    DEFALUT_SAVEPATH = os.path.join(os.getcwd(), 'wxauto文件')


class WeChatBase:
    def _lang(self, text, langtype='MAIN'):
        if langtype == 'MAIN':
            return MAIN_LANGUAGE[text][self.language]
        elif langtype == 'WARNING':
            return WARNING[text][self.language]

    def _split(self, MsgItem):
        uia.SetGlobalSearchTimeout(0)
        MsgItemName = MsgItem.Name
        if MsgItem.BoundingRectangle.height() == WxParam.SYS_TEXT_HEIGHT:
            Msg = ['SYS', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()])]
        elif MsgItem.BoundingRectangle.height() == WxParam.TIME_TEXT_HEIGHT:
            Msg = ['Time', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()])]
        elif MsgItem.BoundingRectangle.height() == WxParam.RECALL_TEXT_HEIGHT:
            if '撤回' in MsgItemName:
                Msg = ['Recall', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()])]
            else:
                Msg = ['SYS', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()])]
        else:
            Index = 1
            User = MsgItem.ButtonControl(foundIndex=Index)
            try:
                while True:
                    if User.Name == '':
                        Index += 1
                        User = MsgItem.ButtonControl(foundIndex=Index)
                    else:
                        break
                winrect = MsgItem.BoundingRectangle
                mid = (winrect.left + winrect.right) / 2
                if User.BoundingRectangle.left < mid:
                    if MsgItem.TextControl().Exists(
                            0.1) and MsgItem.TextControl().BoundingRectangle.top < User.BoundingRectangle.top:
                        name = (User.Name, MsgItem.TextControl().Name)
                    else:
                        name = (User.Name, User.Name)
                else:
                    name = 'Self'
                Msg = [name, MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()])]
            except:
                Msg = ['SYS', MsgItemName, ''.join([str(i) for i in MsgItem.GetRuntimeId()])]
        uia.SetGlobalSearchTimeout(10.0)
        return ParseMessage(Msg, MsgItem, self)

    def _getmsgs(self, msgitems, savepic=False, savefile=False, savevoice=False):
        msgs = []
        for MsgItem in msgitems:
            if MsgItem.ControlTypeName == 'ListItemControl':
                msgs.append(self._split(MsgItem))

        msgtypes = [
            f"[{self._lang('图片')}]",
            f"[{self._lang('文件')}]",
            f"[{self._lang('语音')}]",
        ]

        if not [i for i in msgs if i.content[:4] in msgtypes]:
            return msgs

        for msg in msgs:
            if msg.type not in ('friend', 'self'):
                continue
            if msg.content.startswith(f"[{self._lang('图片')}]") and savepic:
                imgpath = self._download_pic(msg.control)
                msg.content = imgpath if imgpath else msg.content
            elif msg.content.startswith(f"[{self._lang('文件')}]") and savefile:
                filepath = self._download_file(msg.control)
                msg.content = filepath if filepath else msg.content
            elif msg.content.startswith(f"[{self._lang('语音')}]") and savevoice:
                voice_text = self._get_voice_text(msg.control)
                msg.content = voice_text if voice_text else msg.content
            msg.info[1] = msg.content
        return msgs

    def _download_pic(self, msgitem):
        self._show()
        imgcontrol = msgitem.ButtonControl(Name='')
        if not imgcontrol.Exists(0.5):
            return None
        RollIntoView(self.C_MsgList, imgcontrol)
        imgcontrol.Click(simulateMove=False)
        imgobj = WeChatImage()
        savepath = imgobj.Save()
        imgobj.Close()
        return savepath

    def _download_file(self, msgitem):
        filecontrol = msgitem.ButtonControl(Name='')
        if not filecontrol.Exists(0.5):
            return None
        RollIntoView(self.C_MsgList, filecontrol)
        filecontrol.RightClick(simulateMove=False)
        menu = self.UiaAPI.MenuControl(ClassName='CMenuWnd')
        options = [i for i in menu.ListControl().GetChildren() if i.ControlTypeName == 'MenuItemControl']

        copy = [i for i in options if i.Name == '复制']
        if copy:
            copy[0].Click(simulateMove=False)
        else:
            filecontrol.RightClick(simulateMove=False)
            filecontrol.Click(simulateMove=False)
            filewin = self.UiaAPI.WindowControl(ClassName='MsgFileWnd')
            accept_button = filewin.ButtonControl(Name='接收文件')
            if accept_button.Exists(2):
                accept_button.Click(simulateMove=False)

            while True:
                try:
                    filecontrol = msgitem.ButtonControl(Name='')
                    filecontrol.RightClick(simulateMove=False)
                    menu = self.UiaAPI.MenuControl(ClassName='CMenuWnd')
                    options = [i for i in menu.ListControl().GetChildren() if i.ControlTypeName == 'MenuItemControl']
                    copy = [i for i in options if i.Name == '复制']
                    if copy:
                        copy[0].Click(simulateMove=False)
                        break
                    else:
                        filecontrol.RightClick(simulateMove=False)
                except:
                    pass
        filepath = ReadClipboardData().get('15')[0]
        savepath = os.path.join(WxParam.DEFALUT_SAVEPATH, os.path.split(filepath)[1])
        if not os.path.exists(WxParam.DEFALUT_SAVEPATH):
            os.makedirs(WxParam.DEFALUT_SAVEPATH)
        shutil.copyfile(filepath, savepath)
        return savepath

    def _get_voice_text(self, msgitem):
        if msgitem.GetProgenyControl(8, 4):
            return msgitem.GetProgenyControl(8, 4).Name
        voicecontrol = msgitem.ButtonControl(Name='')
        if not voicecontrol.Exists(0.5):
            return None
        RollIntoView(self.C_MsgList, voicecontrol)
        msgitem.GetProgenyControl(7, 1).RightClick(simulateMove=False)
        menu = self.UiaAPI.MenuControl(ClassName='CMenuWnd')
        option = menu.MenuItemControl(Name="语音转文字")
        if not option.Exists(0.5):
            voicecontrol.Click(simulateMove=False)
            if not msgitem.GetProgenyControl(8, 4):
                return None
        else:
            option.Click(simulateMove=False)

        text = ''
        while True:
            if msgitem.GetProgenyControl(8, 4):
                if msgitem.GetProgenyControl(8, 4).Name == text:
                    return text
                text = msgitem.GetProgenyControl(8, 4).Name
            time.sleep(0.1)


class ChatWnd(WeChatBase):
    def __init__(self, who, language='cn'):
        self.who = who
        self.language = language
        self.usedmsgid = []
        
        # 初始化时增加重试机制
        max_init_retries = 3
        for attempt in range(max_init_retries):
            try:
                self.UiaAPI = uia.WindowControl(searchDepth=1, ClassName='ChatWnd', Name=who)
                if not self.UiaAPI.Exists(maxSearchSeconds=3):
                    raise RuntimeError(f"聊天窗口未找到: {who}")
                    
                self.editbox = self.UiaAPI.EditControl()
                if not self.editbox.Exists(maxSearchSeconds=2):
                    raise RuntimeError("输入框控件未找到")
                    
                self.C_MsgList = self.UiaAPI.ListControl()
                if not self.C_MsgList.Exists(maxSearchSeconds=2):
                    raise RuntimeError("消息列表控件未找到")
                    
                op(f"[*] 聊天窗口初始化成功: {who}")
                break
                
            except Exception as e:
                op(f"[!] 聊天窗口初始化失败 {attempt + 1}/{max_init_retries}: {str(e)}")
                if attempt < max_init_retries - 1:
                    time.sleep(1)
                    continue
                else:
                    raise RuntimeError(f"聊天窗口初始化最终失败: {who} - {str(e)}")
        
        self.GetAllMessage()
        self.savepic = False  # 该参数用于在自动监听的情况下是否自动保存聊天图片

    def __repr__(self) -> str:
        return f"<wxauto Chat Window at {hex(id(self))} for {self.who}>"

    def _show(self, max_retries=3):
        """显示并激活聊天窗口，增加重试机制"""
        for attempt in range(max_retries):
            try:
                self.HWND = FindWindow(name=self.who, classname='ChatWnd')
                if not self.HWND:
                    op(f"[!] 尝试 {attempt + 1}/{max_retries}: 未找到窗体 {self.who}")
                    time.sleep(1)
                    continue
                    
                # 显示窗口
                win32gui.ShowWindow(self.HWND, 1)
                win32gui.SetWindowPos(self.HWND, -1, 0, 0, 0, 0, 3)
                win32gui.SetWindowPos(self.HWND, -2, 0, 0, 0, 0, 3)
                
                # 切换到窗口并验证
                self.UiaAPI.SwitchToThisWindow()
                
                # 验证窗口是否可用并等待UI准备就绪
                if self.UiaAPI.Exists(maxSearchSeconds=2) and self._wait_for_ui_ready():
                    op(f"[*] 成功激活窗体：{self.who}")
                    return True
                else:
                    op(f"[!] 尝试 {attempt + 1}/{max_retries}: 窗体激活失败或UI未就绪 {self.who}")
                    
            except Exception as e:
                op(f"[!] 尝试 {attempt + 1}/{max_retries}: 显示窗体异常 {self.who} - {str(e)}")
                
            if attempt < max_retries - 1:
                time.sleep(1)
                
        raise RuntimeError(f"无法显示或激活聊天窗体：{self.who}")

    def AtAll(self, msg=None, password=None):
        """@所有人

        Args:
            msg (str, optional): 要发送的文本消息
            password (str, optional): 屏幕解锁密码
        """
        op(f"[*] @所有人：{self.who} --> {msg}")
        
        # 准备环境（包括屏幕解锁）
        if not self._prepare_environment(password):
            raise RuntimeError(f"无法准备@所有人功能的窗体环境")
            
        # 确保输入框获得焦点
        focus_retries = getattr(config, 'FOCUS_RETRIES', 5)
        focus_delay = getattr(config, 'FOCUS_RETRY_DELAY', 0.3)
        
        for attempt in range(focus_retries):
            try:
                if self.editbox.HasKeyboardFocus:
                    break
                self.editbox.Click(simulateMove=False)
                time.sleep(focus_delay)
            except Exception as e:
                op(f"[!] @所有人获取焦点异常: {str(e)}")
                if attempt < focus_retries - 1:
                    time.sleep(getattr(config, 'FOCUS_FAILURE_DELAY', 0.5))
                    continue
                else:
                    raise RuntimeError(f"@所有人无法获取输入框焦点: {str(e)}")
        
        try:
            self.editbox.SendKeys('@')
            atwnd = self.UiaAPI.PaneControl(ClassName='ChatContactMenu')
            at_all_timeout = getattr(config, 'AT_ALL_MENU_TIMEOUT', 1)
            
            if atwnd.Exists(maxSearchSeconds=at_all_timeout):
                atwnd.ListItemControl(Name='所有人').Click(simulateMove=False)
                if msg:
                    if not msg.startswith('\n'):
                        msg = '\n' + msg
                    self.SendMsg(msg, password=password)
                else:
                    self.editbox.SendKeys('{Enter}')
            else:
                op(f"[!] @所有人菜单未出现，可能当前聊天不支持@所有人功能")
                raise RuntimeError("@所有人菜单未找到")
        except Exception as e:
            op(f"[!] @所有人功能执行失败: {str(e)}")
            raise

    # def SendMsg(self, msg, at=None):
    #     """发送文本消息
    #     Args:
    #         msg (str): 要发送的文本消息
    #         at (str|list, optional): 要@的人，可以是一个人或多个人，格式为str或list，例如："张三"或["张三", "李四"]
    #     """
    #     op(f"[*] 发送消息：{self.who} --> {msg}")
    #     self._show()
    #
    #     # 示例：添加重试逻辑确保输入框获得焦点
    #     max_retries = 3
    #     for attempt in range(max_retries):
    #         if self.editbox.HasKeyboardFocus:
    #             break
    #         self.editbox.Click(simulateMove=False)
    #         time.sleep(0.5)  # 等待一小段时间让UI响应
    #     else:
    #         raise RuntimeError("无法获取输入框焦点")
    #
    #
    #     # if not self.editbox.HasKeyboardFocus:
    #     #     self.editbox.Click(simulateMove=False)
    #     #     op(f"[*] 发送消息：self.editbox.Click(simulateMove=False)")
    #
    #     if at:
    #         if isinstance(at, str):
    #             at = [at]
    #         for i in at:
    #             self.editbox.SendKeys('@'+i)
    #             atwnd = self.UiaAPI.PaneControl(ClassName='ChatContactMenu')
    #             if atwnd.Exists(maxSearchSeconds=0.1):
    #                 atwnd.SendKeys('{ENTER}')
    #                 if msg and not msg.startswith('\n'):
    #                     msg = '\n' + msg
    #
    #     t0 = time.time()
    #     while True:
    #         if time.time() - t0 > 10:
    #             op(f"[*]发送消息超时 --> {self.who} - {msg}")
    #             raise TimeoutError(f'发送消息超时')
    #         SetClipboardText(msg)
    #         op(f"[*]发送消息超时 -->SetClipboardText - ")
    #         self.editbox.SendKeys('{Ctrl}v')
    #         op(f"[*]发送消息超时 -->CtrlV - ")
    #         if self.editbox.GetValuePattern().Value:
    #             op(f"[*]发送消息超时 -->123 - {self.editbox.GetValuePattern().Value}")
    #             break
    #     self.editbox.SendKeys('{Enter}')
    #     op(f"[*]发送消息超时 -->enter")

    # 其他代码保持不变

    def _refresh_controls(self):
        """刷新UI控件引用，防止控件失效"""
        try:
            # 重新获取UI控件引用
            self.UiaAPI = uia.WindowControl(searchDepth=1, ClassName='ChatWnd', Name=self.who)
            self.editbox = self.UiaAPI.EditControl()
            self.C_MsgList = self.UiaAPI.ListControl()
            
            # 验证控件是否可用
            if not self.editbox.Exists(maxSearchSeconds=2):
                raise RuntimeError("输入框控件获取失败")
            if not self.C_MsgList.Exists(maxSearchSeconds=2):
                raise RuntimeError("消息列表控件获取失败")
                
            op(f"[*] 成功刷新UI控件：{self.who}")
            return True
        except Exception as e:
            op(f"[!] 刷新UI控件失败：{self.who} - {str(e)}")
            return False
    
    def _is_window_available(self):
        """检查窗口是否可用"""
        try:
            if not self.HWND or not win32gui.IsWindow(self.HWND):
                return False
            if not win32gui.IsWindowVisible(self.HWND):
                return False
            return True
        except:
            return False
    
    def _wait_for_ui_ready(self, timeout=5):
        """等待UI准备就绪"""
        start_time = time.time()
        while time.time() - start_time < timeout:
            try:
                if self.editbox.Exists(maxSearchSeconds=0.5) and self.C_MsgList.Exists(maxSearchSeconds=0.5):
                    return True
            except:
                pass
            time.sleep(0.2)
        return False
    
    def _check_wechat_process(self):
        """检查微信进程是否正常运行"""
        try:
            import psutil
            for proc in psutil.process_iter(['pid', 'name']):
                if 'WeChat' in proc.info['name']:
                    op(f"[*] 微信进程运行正常: {proc.info}")
                    return True
            op(f"[!] 未找到微信进程")
            return False
        except ImportError:
            # 如果没有psutil，使用Windows命令
            try:
                import subprocess
                result = subprocess.run(['tasklist', '/FI', 'IMAGENAME eq WeChat.exe'], 
                                      capture_output=True, text=True)
                if 'WeChat.exe' in result.stdout:
                    op(f"[*] 微信进程运行正常")
                    return True
                else:
                    op(f"[!] 未找到微信进程")
                    return False
            except:
                op(f"[!] 无法检查微信进程状态")
                return True  # 假设进程正常，避免阻塞
    
    def _check_and_unlock_screen(self, password=None):
        """检查并解锁屏幕
        
        Args:
            password (str, optional): 解锁密码
            
        Returns:
            bool: 解锁是否成功或屏幕未锁定
        """
        if not SCREEN_UNLOCK_AVAILABLE or not config.SCREEN_LOCK_CHECK_ENABLED:
            op(f"[*] 屏幕锁定检查已禁用")
            return True
        
        try:
            # 检查屏幕是否锁定
            if not screen_lock_manager.is_screen_locked():
                return True
            
            op(f"[!] 检测到屏幕已锁定，准备解锁...")
            
            # 如果启用了自动解锁功能
            if config.AUTO_UNLOCK_ENABLED:
                return screen_lock_manager.auto_unlock_with_retry(password)
            else:
                op(f"[!] 自动解锁功能已关闭，请手动解锁屏幕")
                return False
                
        except Exception as e:
            op(f"[!] 检查和解锁屏幕时发生异常: {str(e)}")
            return False
    
    def _prepare_environment(self, password=None):
        """准备发送消息的环境
        
        Args:
            password (str, optional): 屏幕解锁密码
            
        Returns:
            bool: 环境准备是否成功
        """
        try:
            # 1. 检查微信进程
            if config.PROCESS_CHECK_ENABLED and not self._check_wechat_process():
                raise RuntimeError("微信进程未运行")
            
            # 2. 检查并解锁屏幕
            if not self._check_and_unlock_screen(password):
                raise RuntimeError("屏幕解锁失败")
            
            # 3. 显示窗体
            if not self._show():
                raise RuntimeError("无法显示聊天窗体")
            
            # 4. 刷新控件
            if not self._refresh_controls():
                raise RuntimeError("无法刷新UI控件")
            
            return True
            
        except Exception as e:
            op(f"[!] 环境准备失败: {str(e)}")
            return False

    def SendMsg(self, msg, at=None, password=None):
        """发送文本消息
        Args:
            msg (str): 要发送的文本消息
            at (str|list, optional): 要@的人，可以是一个人或多个人，格式为str或list，例如："张三"或["张三", "李四"]
            password (str, optional): 解锁屏幕所需的密码
        """
        op(f"[*] 发送消息：{self.who} --> {msg}")
        
        # 最多重试3次
        max_retries = getattr(config, 'MESSAGE_SEND_RETRIES', 3)
        retry_delay = getattr(config, 'MESSAGE_SEND_RETRY_DELAY', 2)
        
        for retry_count in range(max_retries):
            try:
                # 准备环境（包括屏幕解锁）
                if not self._prepare_environment(password):
                    if retry_count < max_retries - 1:
                        op(f"[*] 环境准备失败，等待 {retry_delay} 秒后重试...")
                        time.sleep(retry_delay)
                        continue
                    else:
                        raise RuntimeError("无法准备发送消息的环境")

                # 尝试获取输入框焦点
                focus_retries = getattr(config, 'FOCUS_RETRIES', 5)
                focus_delay = getattr(config, 'FOCUS_RETRY_DELAY', 0.3)
                
                for attempt in range(focus_retries):
                    try:
                        if self.editbox.HasKeyboardFocus:
                            break
                        self.editbox.Click(simulateMove=False)
                        time.sleep(focus_delay)
                    except Exception as e:
                        op(f"[!] 获取焦点异常 {attempt + 1}/{focus_retries}: {str(e)}")
                        if attempt < focus_retries - 1:
                            time.sleep(getattr(config, 'FOCUS_FAILURE_DELAY', 0.5))
                            continue
                        else:
                            raise RuntimeError(f"无法获取输入框焦点: {str(e)}")
                else:
                    if not self.editbox.HasKeyboardFocus:
                        raise RuntimeError("输入框无法获得焦点")

                # 处理@功能
                if at:
                    if isinstance(at, str):
                        at = [at]
                    for i in at:
                        try:
                            self.editbox.SendKeys('@' + i)
                            atwnd = self.UiaAPI.PaneControl(ClassName='ChatContactMenu')
                            at_timeout = getattr(config, 'AT_MENU_TIMEOUT', 0.5)
                            if atwnd.Exists(maxSearchSeconds=at_timeout):
                                atwnd.SendKeys('{ENTER}')
                                if msg and not msg.startswith('\n'):
                                    msg = '\n' + msg
                        except Exception as e:
                            op(f"[!] @功能异常: {str(e)}")

                # 发送消息内容
                message_sent = False
                content_timeout = getattr(config, 'MESSAGE_CONTENT_TIMEOUT', 15)
                content_retry_delay = getattr(config, 'MESSAGE_CONTENT_RETRY_DELAY', 0.5)
                
                t0 = time.time()
                while True:
                    if time.time() - t0 > content_timeout:
                        raise TimeoutError(f'发送消息内容超时')
                        
                    try:
                        SetClipboardText(msg)
                        op(f"[*] 设置剪贴板内容完成")
                        
                        self.editbox.SendKeys('{Ctrl}v')
                        op(f"[*] 执行粘贴操作完成")
                        
                        # 验证内容是否成功粘贴
                        time.sleep(getattr(config, 'CLIPBOARD_PROCESS_DELAY', 0.2))
                        current_value = self.editbox.GetValuePattern().Value
                        if current_value and current_value.strip():
                            op(f"[*] 消息内容已成功填入: {current_value[:50]}...")
                            message_sent = True
                            break
                        else:
                            op(f"[!] 消息内容填入失败，重试...")
                            time.sleep(content_retry_delay)
                            
                    except Exception as e:
                        op(f"[!] 发送消息过程异常: {str(e)}")
                        time.sleep(content_retry_delay)

                if not message_sent:
                    raise RuntimeError("无法将消息内容填入输入框")

                # 发送消息
                try:
                    self.editbox.SendKeys('{Enter}')
                    op(f"[*] 消息发送完成: {self.who}")
                    return True  # 成功发送，退出重试循环
                    
                except Exception as e:
                    raise RuntimeError(f"发送消息按键异常: {str(e)}")
                    
            except Exception as e:
                op(f"[!] 发送消息第 {retry_count + 1} 次尝试失败: {str(e)}")
                if retry_count < max_retries - 1:
                    op(f"[*] 等待 {retry_delay} 秒后重试...")
                    time.sleep(retry_delay)
                    continue
                else:
                    op(f"[!] 发送消息最终失败: {self.who} --> {msg}")
                    raise TimeoutError(f'发送消息最终失败: {str(e)}')

    def SendFiles(self, filepath):
        """向当前聊天窗口发送文件

        Args:
            filepath (str|list): 要复制文件的绝对路径

        Returns:
            bool: 是否成功发送文件
        """
        op(f"[*] 发送文件：{self.who} --> {filepath}")
        filelist = []
        if isinstance(filepath, str):
            if not os.path.exists(filepath):
                Warnings.lightred(f'未找到文件：{filepath}，无法成功发送', stacklevel=2)
                return False
            else:
                filelist.append(os.path.realpath(filepath))
        elif isinstance(filepath, (list, tuple, set)):
            for i in filepath:
                if os.path.exists(i):
                    filelist.append(i)
                else:
                    Warnings.lightred(f'未找到文件：{i}', stacklevel=2)
        else:
            Warnings.lightred(f'filepath参数格式错误：{type(filepath)}，应为str、list、tuple、set格式', stacklevel=2)
            return False

        if filelist:
            # 显示窗体并刷新控件
            if not self._show() or not self._refresh_controls():
                op(f"[!] 发送文件时无法准备窗体环境")
                return False
                
            try:
                self.editbox.SendKeys('{Ctrl}a', waitTime=0)
                t0 = time.time()
                file_sent = False
                
                while True:
                    if time.time() - t0 > 15:  # 增加超时时间
                        raise TimeoutError(f'发送文件超时 --> {filelist}')
                        
                    try:
                        SetClipboardFiles(filelist)
                        time.sleep(0.3)  # 给系统时间处理剪贴板
                        self.editbox.SendKeys('{Ctrl}v')
                        time.sleep(0.2)
                        
                        # 检查是否成功填入内容
                        if self.editbox.GetValuePattern().Value:
                            file_sent = True
                            break
                        else:
                            op(f"[!] 文件内容未成功填入，重试...")
                            time.sleep(0.5)
                            
                    except Exception as e:
                        op(f"[!] 发送文件过程异常: {str(e)}")
                        time.sleep(0.5)
                
                if file_sent:
                    self.editbox.SendKeys('{Enter}')
                    op(f"[*] 文件发送成功: {filelist}")
                    return True
                else:
                    op(f"[!] 文件发送失败: {filelist}")
                    return False
                    
            except Exception as e:
                op(f"[!] 发送文件异常: {str(e)}")
                return False
        else:
            Warnings.lightred('所有文件都无法成功发送', stacklevel=2)
            return False

    def GetAllMessage(self, savepic=False, savefile=False, savevoice=False):
        '''获取当前窗口中加载的所有聊天记录

        Args:
            savepic (bool): 是否自动保存聊天图片
            savefile (bool): 是否自动保存聊天文件
            savevoice (bool): 是否自动保存语音转文字

        Returns:
            list: 聊天记录信息
        '''
        op(f"[*] 获取所有聊天记录：{self.who}")
        MsgItems = self.C_MsgList.GetChildren()
        msgs = self._getmsgs(MsgItems, savepic, savefile, savevoice)
        return msgs

    def GetNewMessage(self, savepic=False, savefile=False, savevoice=False):
        '''获取当前窗口中加载的新聊天记录

        Args:
            savepic (bool): 是否自动保存聊天图片
            savefile (bool): 是否自动保存聊天文件
            savevoice (bool): 是否自动保存语音转文字

        Returns:
            list: 新聊天记录信息
        '''
        # op(f"[*] 获取新聊天记录：{self.who}")
        if not self.usedmsgid:
            self.usedmsgid = [i[-1] for i in self.GetAllMessage()]
            return []
        MsgItems = self.C_MsgList.GetChildren()
        NewMsgItems = [i for i in MsgItems if ''.join([str(i) for i in i.GetRuntimeId()]) not in self.usedmsgid]
        if not NewMsgItems:
            return []
        newmsgs = self._getmsgs(NewMsgItems, savepic, savefile, savevoice)
        self.usedmsgid = [i[-1] for i in self._getmsgs(MsgItems)]
        # if newmsgs[0].type == 'sys' and newmsgs[0].content == self._lang('查看更多消息'):
        #     newmsgs = newmsgs[1:]
        return newmsgs

    def LoadMoreMessage(self):
        """加载当前聊天页面更多聊天信息

        Returns:
            bool: 是否成功加载更多聊天信息
        """
        op(f"[*] 加载更多聊天信息：{self.who}")
        self._show()
        loadmore = self.C_MsgList.GetFirstChildControl()
        loadmore_top = loadmore.BoundingRectangle.top
        top = self.C_MsgList.BoundingRectangle.top
        while True:
            if loadmore.BoundingRectangle.top > top or loadmore.Name == '':
                isload = True
                break
            else:
                self.C_MsgList.WheelUp(wheelTimes=10, waitTime=0.1)
                if loadmore.BoundingRectangle.top == loadmore_top:
                    isload = False
                    break
                else:
                    loadmore_top = loadmore.BoundingRectangle.top
        self.C_MsgList.WheelUp(wheelTimes=1, waitTime=0.1)
        return isload

    def GetGroupMembers(self):
        """获取当前聊天群成员

        Returns:
            list: 当前聊天群成员列表
        """
        op(f"[*] 获取当前聊天群成员：{self.who}")
        ele = self.UiaAPI.PaneControl(searchDepth=7, foundIndex=6).ButtonControl(Name='聊天信息')
        try:
            uia.SetGlobalSearchTimeout(1)
            rect = ele.BoundingRectangle
            Click(rect)
        except:
            return
        finally:
            uia.SetGlobalSearchTimeout(10)
        roominfoWnd = self.UiaAPI.WindowControl(ClassName='SessionChatRoomDetailWnd', searchDepth=1)
        more = roominfoWnd.ButtonControl(Name='查看更多', searchDepth=8)
        try:
            uia.SetGlobalSearchTimeout(1)
            rect = more.BoundingRectangle
            Click(rect)
        except:
            pass
        finally:
            uia.SetGlobalSearchTimeout(10)
        members = [i.Name for i in roominfoWnd.ListControl(Name='聊天成员').GetChildren()]
        while members[-1] in ['添加', '移出']:
            members = members[:-1]
        roominfoWnd.SendKeys('{Esc}')
        return members


class WeChatImage:
    def __init__(self, language='cn') -> None:
        self.language = language
        self.api = uia.WindowControl(ClassName='ImagePreviewWnd', searchDepth=1)
        MainControl1 = [i for i in self.api.GetChildren() if not i.ClassName][0]
        self.ToolsBox, self.PhotoBox = MainControl1.GetChildren()

        # tools按钮
        self.t_previous = self.ToolsBox.ButtonControl(Name=self._lang('上一张'))
        self.t_next = self.ToolsBox.ButtonControl(Name=self._lang('下一张'))
        self.t_zoom = self.ToolsBox.ButtonControl(Name=self._lang('放大'))
        self.t_translate = self.ToolsBox.ButtonControl(Name=self._lang('翻译'))
        self.t_ocr = self.ToolsBox.ButtonControl(Name=self._lang('提取文字'))
        self.t_save = self.ToolsBox.ButtonControl(Name=self._lang('另存为...'))
        self.t_qrcode = self.ToolsBox.ButtonControl(Name=self._lang('识别图中二维码'))

    def __repr__(self) -> str:
        return f"<wxauto WeChat Image at {hex(id(self))}>"

    def _lang(self, text):
        return IMAGE_LANGUAGE[text][self.language]

    def _show(self):
        HWND = FindWindow(classname='ImagePreviewWnd')
        win32gui.ShowWindow(HWND, 1)
        self.api.SwitchToThisWindow()

    def OCR(self):
        result = ''
        ctrls = self.PhotoBox.GetChildren()
        if len(ctrls) == 2:
            self.t_ocr.Click(simulateMove=False)
        ctrls = self.PhotoBox.GetChildren()
        if len(ctrls) != 3:
            Warnings.lightred('获取文字识别失败', stacklevel=2)
        else:
            TranslateControl = ctrls[-1]
            result = TranslateControl.TextControl().Name
        return result

    def Save(self, savepath='', timeout=10):
        """保存图片

        Args:
            savepath (str): 绝对路径，包括文件名和后缀，例如："D:/Images/微信图片_xxxxxx.jpg"
            （如果不填，则默认为当前脚本文件夹下，新建一个“微信图片”的文件夹，保存在该文件夹内）

        Returns:
            str: 文件保存路径，即savepath
        """

        if not savepath:
            savepath = os.path.join(WxParam.DEFALUT_SAVEPATH,
                                    f"微信图片_{datetime.datetime.now().strftime('%Y%m%d%H%M%S%f')}.jpg")
        if not os.path.exists(os.path.split(savepath)[0]):
            os.makedirs(os.path.split(savepath)[0])

        if self.t_zoom.Exists(maxSearchSeconds=5):
            self.t_save.Click(simulateMove=False)
        else:
            raise TimeoutError('下载超时')
        t0 = time.time()
        while True:
            if time.time() - t0 > timeout:
                raise TimeoutError('下载超时')
            handle = FindWindow(name='另存为...')
            if handle:
                break
        t0 = time.time()
        while True:
            if time.time() - t0 > timeout:
                raise TimeoutError('下载超时')
            try:
                edithandle = [i for i in GetAllWindowExs(handle) if i[1] == 'Edit' and i[-1]][0][0]
                savehandle = FindWinEx(handle, classname='Button', name='保存(&S)')[0]
                if edithandle and savehandle:
                    break
            except:
                pass
        win32gui.SendMessage(edithandle, win32con.WM_SETTEXT, '', str(savepath))
        win32gui.SendMessage(savehandle, win32con.BM_CLICK, 0, 0)
        return savepath

    def Previous(self):
        """上一张"""
        if self.t_previous.IsKeyboardFocusable:
            self._show()
            self.t_previous.Click(simulateMove=False)
            return True
        else:
            Warnings.lightred('上一张按钮不可用', stacklevel=2)
            return False

    def Next(self, warning=True):
        """下一张"""
        if self.t_next.IsKeyboardFocusable:
            self._show()
            self.t_next.Click(simulateMove=False)
            return True
        else:
            if warning:
                Warnings.lightred('已经是最新的图片了', stacklevel=2)
            return False

    def Close(self):
        self._show()
        self.api.SendKeys('{Esc}')


class TextElement:
    def __init__(self, ele, wx) -> None:
        self._wx = wx
        chatname = wx.CurrentChat()
        self.ele = ele
        self.sender = ele.ButtonControl(foundIndex=1, searchDepth=2)
        _ = ele.GetFirstChildControl().GetChildren()[1].GetChildren()
        if len(_) == 1:
            self.content = _[0].TextControl().Name
            self.chattype = 'friend'
            self.chatname = chatname
        else:
            self.sender_remark = _[0].TextControl().Name
            self.content = _[1].TextControl().Name
            self.chattype = 'group'
            numtext = re.findall(' \(\d+\)', chatname)[-1]
            self.chatname = chatname[:-len(numtext)]

        self.info = {
            'sender': self.sender.Name,
            'content': self.content,
            'chatname': self.chatname,
            'chattype': self.chattype,
            'sender_remark': self.sender_remark if hasattr(self, 'sender_remark') else ''
        }

    def __repr__(self) -> str:
        return f"<wxauto Text Element at {hex(id(self))} ({self.sender.Name}: {self.content})>"


class NewFriendsElement:
    def __init__(self, ele, wx):
        self._wx = wx
        self.ele = ele
        self.name = self.ele.Name
        self.msg = self.ele.GetFirstChildControl().PaneControl(SearchDepth=1).GetChildren()[-1].TextControl().Name
        self.ele.GetChildren()[-1]
        self.Status = self.ele.GetFirstChildControl().GetChildren()[-1]
        self.acceptable = False
        if isinstance(self.Status, uia.ButtonControl):
            self.acceptable = True

    def __repr__(self) -> str:
        return f"<wxauto New Friends Element at {hex(id(self))} ({self.name}: {self.msg})>"

    def Accept(self, remark=None, tags=None):
        """接受好友请求

        Args:
            remark (str, optional): 备注名
            tags (list, optional): 标签列表
        """
        op(f"[*] 接受好友请求：{self.name}  备注：{remark} 标签：{tags}")
        self._wx._show()
        self.Status.Click(simulateMove=False)
        NewFriendsWnd = self._wx.UiaAPI.WindowControl(ClassName='WeUIDialog')

        if remark:
            remarkedit = NewFriendsWnd.TextControl(Name='备注名').GetParentControl().EditControl()
            remarkedit.Click(simulateMove=False)
            remarkedit.SendKeys('{Ctrl}a', waitTime=0)
            remarkedit.SendKeys(remark)

        if tags:
            tagedit = NewFriendsWnd.TextControl(Name='标签').GetParentControl().EditControl()
            for tag in tags:
                tagedit.Click(simulateMove=False)
                tagedit.SendKeys(tag)
                NewFriendsWnd.PaneControl(ClassName='DropdownWindow').TextControl().Click(simulateMove=False)

        NewFriendsWnd.ButtonControl(Name='确定').Click(simulateMove=False)


class ContactWnd:
    def __init__(self):
        self.UiaAPI = uia.WindowControl(ClassName='ContactManagerWindow', searchDepth=1)
        self.Sidebar, _, self.ContactBox = self.UiaAPI.PaneControl(ClassName='', searchDepth=3,
                                                                   foundIndex=3).GetChildren()

    def __repr__(self) -> str:
        return f"<wxauto Contact Window at {hex(id(self))}>"

    def _show(self):
        self.HWND = FindWindow(classname='ContactManagerWindow')
        win32gui.ShowWindow(self.HWND, 1)
        win32gui.SetWindowPos(self.HWND, -1, 0, 0, 0, 0, 3)
        win32gui.SetWindowPos(self.HWND, -2, 0, 0, 0, 0, 3)
        self.UiaAPI.SwitchToThisWindow()

    def GetFriendNum(self):
        """获取好友人数"""
        op("[*] 获取好友人数")
        numText = self.Sidebar.PaneControl(Name='全部').TextControl(foundIndex=2).Name
        return int(re.findall('\d+', numText)[0])

    def Search(self, keyword):
        """搜索好友

        Args:
            keyword (str): 搜索关键词
        """
        op(f"[*] 搜索好友：{keyword}")
        self.ContactBox.EditControl(Name="搜索").Click(simulateMove=False)
        self.ContactBox.SendKeys('{Ctrl}{A}')
        self.ContactBox.SendKeys(keyword)

    def GetAllFriends(self):
        """获取好友列表"""
        op("[*] 获取好友列表")
        self._show()
        contacts_list = []
        while True:
            contact_ele_list = self.ContactBox.ListControl().GetChildren()
            for ele in contact_ele_list:
                contacts_info = {
                    'nickname': ele.TextControl().Name,
                    'remark': ele.ButtonControl(foundIndex=2).Name,
                    'tags': ele.ButtonControl(foundIndex=3).Name.split('，'),
                }
                if contacts_info.get('remark') in ('添加备注', ''):
                    contacts_info['remark'] = None
                if contacts_info.get('tags') in (['添加标签'], ['']):
                    contacts_info['tags'] = None
                if contacts_info not in contacts_list:
                    contacts_list.append(contacts_info)
            bottom = self.ContactBox.ListControl().GetChildren()[-1].BoundingRectangle.top
            self.ContactBox.WheelDown(wheelTimes=5, waitTime=0.1)
            if bottom == self.ContactBox.ListControl().GetChildren()[-1].BoundingRectangle.top:
                return contacts_list

    def Close(self):
        """关闭联系人窗口"""
        op("[*] 关闭联系人窗口")
        self._show()
        self.UiaAPI.SendKeys('{Esc}')


class ContactElement:
    def __init__(self, ele):
        self.element = ele
        self.nickname = ele.TextControl().Name
        self.remark = ele.ButtonControl(foundIndex=2).Name
        self.tags = ele.ButtonControl(foundIndex=3).Name.split('，')

    def __repr__(self) -> str:
        return f"<wxauto Contact Element at {hex(id(self))} ({self.nickname}: {self.remark})>"

    def EditRemark(self, remark: str):
        """修改好友备注名

        Args:
            remark (str): 新备注名
        """
        op(f"[*] 修改好友备注名：{self.nickname} --> {remark}")
        self.element.ButtonControl(foundIndex=2).Click(simulateMove=False)
        self.element.SendKeys('{Ctrl}a')
        self.element.SendKeys(remark)
        self.element.SendKeys('{Enter}')


class SessionElement:
    def __init__(self, item):
        self.name = item.GetProgenyControl(4, control_type='TextControl').Name \
            if item.GetProgenyControl(4, control_type='TextControl') else None
        self.time = item.GetProgenyControl(4, 1, control_type='TextControl').Name \
            if item.GetProgenyControl(4, 1, control_type='TextControl') else None
        self.content = item.GetProgenyControl(4, 2, control_type='TextControl').Name \
            if item.GetProgenyControl(4, 2, control_type='TextControl') else None
        self.isnew = item.GetProgenyControl(2, 2) is not None
        op(f"[*] ============== 【{self.name}】 ==============")
        op(f"[*] 最后一条消息时间: {self.time}")
        op(f"[*] 最后一条消息内容: {self.content}")
        op(f"[*] 是否有新消息: {self.isnew}")


class Message:
    type = 'message'

    def __getitem__(self, index):
        return self.info[index]

    def __str__(self):
        return self.content

    def __repr__(self):
        return str(self.info[:2])


class SysMessage(Message):
    type = 'sys'

    def __init__(self, info, control, wx):
        self.info = info
        self.control = control
        self.wx = wx
        self.sender = info[0]
        self.content = info[1]
        self.id = info[-1]
        # op(f"[*] 【系统消息】{self.content}")

    # def __repr__(self):
    #     return f'<wxauto SysMessage at {hex(id(self))}>'


class TimeMessage(Message):
    type = 'time'

    def __init__(self, info, control, wx):
        self.info = info
        self.control = control
        self.wx = wx
        self.time = ParseWeChatTime(info[1])
        self.sender = info[0]
        self.content = info[1]
        self.id = info[-1]
        # op(f"[*] 【时间消息】{self.time}")

    # def __repr__(self):
    #     return f'<wxauto TimeMessage at {hex(id(self))}>'


class RecallMessage(Message):
    type = 'recall'

    def __init__(self, info, control, wx):
        self.info = info
        self.control = control
        self.wx = wx
        self.sender = info[0]
        self.content = info[1]
        self.id = info[-1]
        # op(f"[*] 【撤回消息】{self.content}")

    # def __repr__(self):
    #     return f'<wxauto RecallMessage at {hex(id(self))}>'


class SelfMessage(Message):
    type = 'self'

    def __init__(self, info, control, obj):
        self.info = info
        self.control = control
        self._winobj = obj
        self.sender = info[0]
        self.content = info[1]
        self.id = info[-1]
        self.chatbox = obj.ChatBox if hasattr(obj, 'ChatBox') else obj.UiaAPI
        # op(f"[*] 【自己消息】{self.content}")

    # def __repr__(self):
    #     return f'<wxauto SelfMessage at {hex(id(self))}>'

    def quote(self, msg):
        """引用该消息

        Args:
            msg (str): 引用的消息内容

        Returns:
            bool: 是否成功引用
        """
        op(f"[*] 发送引用消息：{msg}  --> {self.sender} | {self.content}")
        self._winobj._show()
        headcontrol = \
            [i for i in self.control.GetFirstChildControl().GetChildren() if i.ControlTypeName == 'ButtonControl'][0]
        RollIntoView(self.chatbox.ListControl(), headcontrol, equal=True)
        xbias = int(headcontrol.BoundingRectangle.width() * 1.5)
        headcontrol.RightClick(x=-xbias, simulateMove=False)
        menu = self._winobj.UiaAPI.MenuControl(ClassName='CMenuWnd')
        quote_option = menu.MenuItemControl(Name="引用")
        if not quote_option.Exists(maxSearchSeconds=0.1):
            op("[*] 该消息当前状态无法引用")
            return False
        quote_option.Click(simulateMove=False)
        editbox = self.chatbox.EditControl(searchDepth=15)
        t0 = time.time()
        while True:
            if time.time() - t0 > 10:
                raise TimeoutError(f'发送消息超时 --> {msg}')
            SetClipboardText(msg)
            editbox.SendKeys('{Ctrl}v')
            if editbox.GetValuePattern().Value.replace('\r￼', ''):
                break
        editbox.SendKeys('{Enter}')
        return True

    def forward(self, friend):
        """转发该消息

        Args:
            friend (str): 转发给的好友昵称、备注或微信号

        Returns:
            bool: 是否成功转发
        """
        op(f"[*] 转发消息：{self.sender} --> {friend} | {self.content}")
        self._winobj._show()
        headcontrol = \
            [i for i in self.control.GetFirstChildControl().GetChildren() if i.ControlTypeName == 'ButtonControl'][0]
        RollIntoView(self.chatbox.ListControl(), headcontrol, equal=True)
        xbias = int(headcontrol.BoundingRectangle.width() * 1.5)
        headcontrol.RightClick(x=-xbias, simulateMove=False)
        menu = self._winobj.UiaAPI.MenuControl(ClassName='CMenuWnd')
        forward_option = menu.MenuItemControl(Name="转发...")
        if not forward_option.Exists(maxSearchSeconds=0.1):
            op("[*] 该消息当前状态无法转发")
            return False
        forward_option.Click(simulateMove=False)
        SetClipboardText(friend)
        contactwnd = self._winobj.UiaAPI.WindowControl(ClassName='SelectContactWnd')
        contactwnd.SendKeys('{Ctrl}a', waitTime=0)
        contactwnd.SendKeys('{Ctrl}v')
        checkbox = contactwnd.ListControl().CheckBoxControl()
        if checkbox.Exists(1):
            checkbox.Click(simulateMove=False)
            contactwnd.ButtonControl(Name='发送').Click(simulateMove=False)
            return True
        else:
            contactwnd.SendKeys('{Esc}')
            raise FriendNotFoundError(f'未找到好友：{friend}')

    def parse(self):
        """解析合并消息内容，当且仅当消息内容为合并转发的消息时有效"""
        op(f"[*] 解析合并消息内容：{self.sender} | {self.content}")
        self._winobj._show()
        headcontrol = \
            [i for i in self.control.GetFirstChildControl().GetChildren() if i.ControlTypeName == 'ButtonControl'][0]
        RollIntoView(self.chatbox.ListControl(), headcontrol, equal=True)
        xbias = int(headcontrol.BoundingRectangle.width() * 1.5)
        headcontrol.Click(x=-xbias, simulateMove=False)
        chatrecordwnd = uia.WindowControl(ClassName='ChatRecordWnd', searchDepth=1)
        msgitems = chatrecordwnd.ListControl().GetChildren()
        msgs = []
        for msgitem in msgitems:
            textcontrols = [i for i in GetAllControl(msgitem) if i.ControlTypeName == 'TextControl']
            who = textcontrols[0].Name
            time = textcontrols[1].Name
            try:
                content = textcontrols[2].Name
            except IndexError:
                content = ''
            msgs.append(([who, content, ParseWeChatTime(time)]))
        chatrecordwnd.SendKeys('{Esc}')
        return msgs


class FriendMessage(Message):
    type = 'friend'

    def __init__(self, info, control, obj):
        self.info = info
        self.control = control
        self._winobj = obj
        self.sender = info[0][0]
        self.sender_remark = info[0][1]
        self.content = info[1]
        self.id = info[-1]
        self.info[0] = info[0][0]
        self.chatbox = obj.ChatBox if hasattr(obj, 'ChatBox') else obj.UiaAPI
        if self.sender == self.sender_remark:
            op(f"[*] 【好友消息】{self.sender}: {self.content}")
        else:
            op(f"[*] 【好友消息】{self.sender}({self.sender_remark}): {self.content}")

    # def __repr__(self):
    #     return f'<wxauto FriendMessage at {hex(id(self))}>'

    def quote(self, msg):
        """引用该消息

        Args:
            msg (str): 引用的消息内容

        Returns:
            bool: 是否成功引用
        """
        op(f"[*] 发送引用消息：{msg}  --> {self.sender} | {self.content}")
        self._winobj._show()
        headcontrol = \
            [i for i in self.control.GetFirstChildControl().GetChildren() if i.ControlTypeName == 'ButtonControl'][0]
        RollIntoView(self.chatbox.ListControl(), headcontrol, equal=True)
        xbias = int(headcontrol.BoundingRectangle.width() * 1.5)
        headcontrol.RightClick(x=xbias, simulateMove=False)
        menu = self._winobj.UiaAPI.MenuControl(ClassName='CMenuWnd')
        quote_option = menu.MenuItemControl(Name="引用")
        if not quote_option.Exists(maxSearchSeconds=0.1):
            op("[*] 该消息当前状态无法引用")
            return False
        quote_option.Click(simulateMove=False)
        editbox = self.chatbox.EditControl(searchDepth=15)
        t0 = time.time()
        while True:
            if time.time() - t0 > 10:
                raise TimeoutError(f'发送消息超时 --> {msg}')
            SetClipboardText(msg)
            editbox.SendKeys('{Ctrl}v')
            if editbox.GetValuePattern().Value.replace('\r￼', ''):
                break
        editbox.SendKeys('{Enter}')
        return True

    def forward(self, friend):
        """转发该消息

        Args:
            friend (str): 转发给的好友昵称、备注或微信号

        Returns:
            bool: 是否成功转发
        """
        op(f"[*] 转发消息：{self.sender} --> {friend} | {self.content}")
        self._winobj._show()
        headcontrol = \
            [i for i in self.control.GetFirstChildControl().GetChildren() if i.ControlTypeName == 'ButtonControl'][0]
        RollIntoView(self.chatbox.ListControl(), headcontrol, equal=True)
        xbias = int(headcontrol.BoundingRectangle.width() * 1.5)
        headcontrol.RightClick(x=xbias, simulateMove=False)
        menu = self._winobj.UiaAPI.MenuControl(ClassName='CMenuWnd')
        forward_option = menu.MenuItemControl(Name="转发...")
        if not forward_option.Exists(maxSearchSeconds=0.1):
            op("[*] 该消息当前状态无法转发")
            return False
        forward_option.Click(simulateMove=False)
        SetClipboardText(friend)
        contactwnd = self._winobj.UiaAPI.WindowControl(ClassName='SelectContactWnd')
        contactwnd.SendKeys('{Ctrl}a', waitTime=0)
        contactwnd.SendKeys('{Ctrl}v')
        checkbox = contactwnd.ListControl().CheckBoxControl()
        if checkbox.Exists(1):
            checkbox.Click(simulateMove=False)
            contactwnd.ButtonControl(Name='发送').Click(simulateMove=False)
            return True
        else:
            contactwnd.SendKeys('{Esc}')
            raise FriendNotFoundError(f'未找到好友：{friend}')

    def parse(self):
        """解析合并消息内容，当且仅当消息内容为合并转发的消息时有效"""
        op(f"[*] 解析合并消息内容：{self.sender} | {self.content}")
        self._winobj._show()
        headcontrol = \
            [i for i in self.control.GetFirstChildControl().GetChildren() if i.ControlTypeName == 'ButtonControl'][0]
        RollIntoView(self.chatbox.ListControl(), headcontrol, equal=True)
        xbias = int(headcontrol.BoundingRectangle.width() * 1.5)
        headcontrol.Click(x=xbias, simulateMove=False)
        chatrecordwnd = uia.WindowControl(ClassName='ChatRecordWnd', searchDepth=1)
        msgitems = chatrecordwnd.ListControl().GetChildren()
        msgs = []
        for msgitem in msgitems:
            textcontrols = [i for i in GetAllControl(msgitem) if i.ControlTypeName == 'TextControl']
            who = textcontrols[0].Name
            time = textcontrols[1].Name
            try:
                content = textcontrols[2].Name
            except IndexError:
                content = ''
            msgs.append(([who, content, ParseWeChatTime(time)]))
        chatrecordwnd.SendKeys('{Esc}')
        return msgs


message_types = {
    'SYS': SysMessage,
    'Time': TimeMessage,
    'Recall': RecallMessage,
    'Self': SelfMessage
}


def ParseMessage(data, control, wx):
    return message_types.get(data[0], FriendMessage)(data, control, wx)


class LoginWnd:
    _class_name = 'WeChatLoginWndForPC'
    UiaAPI = uia.PaneControl(ClassName=_class_name, searchDepth=1)

    def __repr__(self) -> str:
        return f"<wxauto LoginWnd Object at {hex(id(self))}>"

    def _show(self):
        self.HWND = FindWindow(classname=self._class_name)
        win32gui.ShowWindow(self.HWND, 1)
        win32gui.SetWindowPos(self.HWND, -1, 0, 0, 0, 0, 3)
        win32gui.SetWindowPos(self.HWND, -2, 0, 0, 0, 0, 3)
        self.UiaAPI.SwitchToThisWindow()

    @property
    def _app_path(self):
        HWND = FindWindow(classname=self._class_name)
        return GetPathByHwnd(HWND)

    def login(self):
        enter_button = self.UiaAPI.ButtonControl(Name='进入微信')
        if enter_button.Exists():
            enter_button.Click(simulateMove=False)

    def get_qrcode(self):
        """获取登录二维码

        Returns:
            str: 二维码图片的保存路径
        """
        switch_account_button = self.UiaAPI.ButtonControl(Name='切换账号')
        if switch_account_button.Exists():
            switch_account_button.Click(simulateMove=False)
        self._show()
        qrcode_control = self.UiaAPI.ButtonControl(Name='二维码')
        qrcode = qrcode_control.ScreenShot()
        return qrcode
